package com.手写线程池.线程池消费端;

import com.手写线程池.拒绝策略设计.RejectPolicy;
import com.手写线程池.阻塞队列.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.Objects;
import java.util.Set;


/**
 * @author Fang Ruichuan
 * @date 2023-01-28 21:10
 */
public class ThreadPool implements Executor {

    private Logger logger = LoggerFactory.getLogger(ThreadPool.class);

    // 任务队列
    private BlockingQueue<Runnable> taskQueue;

    // 核心工作线程数
    private int coreSize;

    // 工作线程集合
    private final Set<Worker> workers = new HashSet<>();

    // 拒绝策略
    private RejectPolicy rejectPolicy;

    public ThreadPool(int coreSize, int capacity, RejectPolicy rejectPolicy) {
        this.coreSize = coreSize;
        this.taskQueue = new BlockingQueue<>(capacity);
        this.rejectPolicy = rejectPolicy;
    }

    // 提交任务执行
    @Override
    public void execute(Runnable task) {
        synchronized (workers) {
            // 如果工作线程数小于阈值，直接开始任务执行
            if (workers.size() < coreSize) {
                Worker worker = new Worker(task);
                workers.add(worker);
                worker.start();
            } else {
                // 如果超过了阈值，加入到队列中
                // taskQueue.put(task);
                // 调用tryPut的方式
                taskQueue.tryPut(rejectPolicy, task);
            }
        }
    }

    // 工作线程，对执行的任务做了一层包装经验
    class Worker extends Thread {
        private Runnable task;

        public Worker(Runnable task) {
            this.task = task;
        }

        @Override
        public void run() {
            // 如果任务不为空，或者可以从队列中获取任务
            while (Objects.nonNull(task) || Objects.nonNull(task = taskQueue.take())) {
                try {
                    task.run();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // 执行完后，设置任务为空
                    task = null;
                }
            }

            // 移除工作线程
            synchronized (workers) {
                logger.debug("remove worker successfully");
                workers.remove(this);
            }
        }
    }
}
